package com.mallcai.bigdata.ladon.service.peekdata.impl;

import com.cake.bigdata.starter.exception.category.BusinessException;
import com.google.common.base.Preconditions;
import com.mallcai.bigdata.ladon.dpl.entity.common.Datasource;
import com.mallcai.bigdata.ladon.dpl.entity.peekdata.*;
import com.mallcai.bigdata.ladon.dpl.entity.system.User;
import com.mallcai.bigdata.ladon.dpl.mapper.peekdata.ModelMapper;
import com.mallcai.bigdata.ladon.dpl.mapper.peekdata.PeekRuleMapper;
import com.mallcai.bigdata.ladon.dpl.vo.DataType;
import com.mallcai.bigdata.ladon.service.common.IDatasourceService;
import com.mallcai.bigdata.ladon.service.peekdata.*;
import com.mallcai.bigdata.ladon.service.peekdata.sql.FilterField;
import com.mallcai.bigdata.ladon.service.peekdata.sql.SelectSqlTemplate;
import com.mallcai.bigdata.ladon.service.peekdata.sql.SimpleSqlField;
import com.mallcai.bigdata.ladon.service.system.IAccountService;
import com.mallcai.bigdata.ladon.utils.CollectionUtils;
import com.mallcai.bigdata.ladon.utils.DateUtils;
import com.mallcai.bigdata.ladon.vo.DataVo;
import com.mallcai.bigdata.ladon.vo.ExportParam;
import com.mallcai.bigdata.ladon.vo.QueryParam;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

import static com.cake.bigdata.starter.exception.error.CommonErrorCode.UNAUTHORIZED;

/**
 * @author feixy
 * @version 1.0
 * @date 2019-07-03 10:15
 */
@Service
@Slf4j
public class PeekQueryService implements IPeekQueryService {


    @Autowired
    private ModelMapper modelMapper;

    @Autowired
    private IModelService iModelService;
    @Autowired
    private IPeekService iPeekService;
    @Autowired
    private IModelMetaService iModelMetaService;

    @Autowired
    private IDatasourceService iDatasourceService;

    @Autowired
    private PeekRuleMapper peekRuleMapper;

    @Autowired
    private IPeekFieldService iPeekFieldService;

    @Autowired
    private IAccountService iAccountService;

    @Override
    public DataVo previewData(Long modelId, List<PeekField> fieldList, List<PeekRule> ruleList) {
        Model model = Preconditions.checkNotNull(iModelService.getById(modelId), "取数依赖的模型不存在") ;

        Map<String, String> showNames = CollectionUtils.toMap(fieldList,PeekField::getMetaName, PeekField::getShowName);

        Map<String, String> displayDataTypes = CollectionUtils.toMap(fieldList, PeekField::getMetaName, AggDataTypeUtils::calculateAggDataType);

        String sql = createQuerySql(model,fieldList, ruleList);
        QueryParam params = new QueryParam();
        params.setNeedLimit(true);
        params.setLimitSize(10);
        params.setRawSql(sql);
        params.setDataTypes(displayDataTypes);
        log.info("query sql :{}", params.getRawSql() );
        DataVo dataVo = iDatasourceService.execSql(model.getDatasourceId(), params);
        dataVo.setShowNameOfColumns(showNames);
        return dataVo;
    }

    private String createQuerySql(Model model,List<PeekField> fieldList, List<PeekRule> ruleList) {
        return new SelectSqlTemplate(model.getTableName(),
                    getSqlFieldList(fieldList),
                    getFilterFieldList(ruleList)).toSql();
    }


    @Override
    public Long countDataSize(Long modelId, List<PeekField> fieldList, List<PeekRule> rules) {
        Model model = Preconditions.checkNotNull(modelMapper.getById(modelId), "取数依赖的模型不存在") ;
        String sql = createQuerySql(model,fieldList, rules);
        QueryParam params = new QueryParam();
        params.setRawSql("select count(1) as size from (" + sql + ") a");
        log.info("query sql :{}", params.getRawSql() );
        DataVo dataVo = iDatasourceService.execSql(model.getDatasourceId(), params);
        if (dataVo != null && dataVo.getRowSize() > 0) {
            Object obj = dataVo.getRows().get(0).get("size");
            if (obj instanceof Long) {
                return (Long) obj;
            } else {
                return Long.valueOf(String.valueOf(obj));
            }
        }
        return 0L;
    }


    private List<SimpleSqlField> getSqlFieldList(List<PeekField> peekFieldList){
        List<ModelMeta> modelMetaList = iModelMetaService.getByIds(CollectionUtils.map(peekFieldList, PeekField::getMetaId));
        Map<Long, ModelMeta> modelMetaMap = CollectionUtils.toMap(modelMetaList,ModelMeta::getId, Function.identity());
        List<SimpleSqlField> fieldList = new ArrayList<>();
        for (PeekField peekField : peekFieldList) {
            ModelMeta modelMeta = modelMetaMap.get(peekField.getMetaId());
            if(modelMeta != null){
                fieldList.add(new SimpleSqlField(modelMeta.getName(), peekField.getAggExpression()));
            }
        }
        return fieldList;
    }


    private List<FilterField> getFilterFieldList(List<PeekRule> ruleList){
        List<ModelMeta> modelMetaList = iModelMetaService.getByIds(CollectionUtils.map(ruleList,PeekRule::getMetaId));
        Map<Long, ModelMeta> modelMetaMap = CollectionUtils.toMap(modelMetaList, ModelMeta::getId, Function.identity());
        return CollectionUtils.map(ruleList,(peekRule)->{
            ModelMeta modelMeta = modelMetaMap.get(peekRule.getMetaId());
            String value =  peekRule.getValue();
            if("bigint".equalsIgnoreCase(modelMeta.getOriginalDataType())){
                DataType dataType = DataType.parse(modelMeta.getDataType());
                if(dataType == DataType.DATETIME){
                    value = String.valueOf(DateUtils.parseDate(value, "yyyy-MM-dd HH:mm:ss").getTime());
                } else if (dataType == DataType.DATE){
                    value = String.valueOf(DateUtils.parseDate(value, "yyyy-MM-dd").getTime());
                } else if(dataType == DataType.TIME){
                    value = String.valueOf(DateUtils.parseDate(value, "HH:mm:ss").getTime());
                }
            }
            String dataType = StringUtils.isEmpty(modelMeta.getOriginalDataType())? modelMeta.getDataType() : modelMeta.getOriginalDataType() ;
           return new FilterField(modelMeta.getName(), peekRule.getRule(), value, dataType);
        });
    }

    @Override
    public int sendData2Me(Long peekId) {
        Peek peek = Preconditions.checkNotNull(iPeekService.getById(peekId), "取数信息不存在");

        Model model = Preconditions.checkNotNull(modelMapper.getById(peek.getModelId()),"模型信息不存在");

        Datasource ds = Preconditions.checkNotNull(iDatasourceService.getById(model.getDatasourceId()), "数据源信息不存在");

        List<PeekRule> ruleList = peekRuleMapper.getByPeekId(peekId);

        List<PeekField> fieldList = iPeekFieldService.getByPeekId(peekId);

        Map<String, String> showNames = CollectionUtils.toMap(fieldList,PeekField::getMetaName, PeekField::getShowName);

        User user = iAccountService.getCurrentUser(); // 获取当前用户
        if (user == null) {
            throw new BusinessException(UNAUTHORIZED.getCode(),"未授权的用户操作");
        }
        if (StringUtils.isBlank(user.getEmail())) {
            throw new BusinessException("403", "用户邮箱为空");
        }

        ExportParam params = new ExportParam();
        params.setTitle(peek.getName()); // 设置标题
        params.setEmail(user.getEmail());
        params.setFieldNameToShow(showNames);
        params.setExportFileType("csv");
        params.setFromName("大数据");
        params.setFrom("sxlfgst@163.com");
        params.setNeedLimit(true);
        params.setLimitSize(60 * 10000); // 限制最多导出60万
        params.setDisplayDataTypes(CollectionUtils.toMap(fieldList, PeekField::getMetaName, modelMeta -> DataType.getByCode(modelMeta.getAggDataType())));

        params.setRawSql(createQuerySql(model, fieldList, ruleList));
        int status = iDatasourceService.asynSendData2Email(ds, params);
        if (status > 0) {
            // 增加取数值
            iPeekService.incPeekNum(peekId);
        }
        return status;
    }
}
